// import { webRTC } from "@libp2p/webrtc"
import { webSockets } from "@libp2p/websockets"
import { mplex } from "@libp2p/mplex"
import { bootstrap } from '@libp2p/bootstrap'
import { createLibp2p } from "libp2p"
import { circuitRelayTransport } from 'libp2p/circuit-relay'
import { noise } from "@chainsafe/libp2p-noise"
import { gossipsub } from '@chainsafe/libp2p-gossipsub'
import { yamux } from '@chainsafe/libp2p-yamux'
import { identifyService } from 'libp2p/identify'
import { preSharedKey } from 'libp2p/pnet'
import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
import Logger from 'logplease'
import { multiaddr } from '@multiformats/multiaddr'
import { peerIdToDid } from '../peer-id.js'
// import { createEd25519PeerId } from './peer-id.js'
import pMap from 'p-map';

const logger = Logger.create('network')
Logger.setLogLevel('DEBUG')


const swarmKey_dev = '/key/swarm/psk/1.0.0/\n/base16/\n' + '7ba72b5b15148919d364693b0a8cd2c36a09a3345f210c96ccc019e2ace1def6'
const RELAY_SERVER = '/dns4/libp.we-linkin.com/tcp/44008/wss/p2p/12D3KooWELrypWhpyUef9gtJr5TUne1173fG24QpMrRN62HN4VDk'
// const RELAY_SERVER = '/ip4/192.168.8.61/tcp/4008/ws/p2p/12D3KooWELrypWhpyUef9gtJr5TUne1173fG24QpMrRN62HN4VDk'
// const RELAY_SERVER = '/ip4/192.168.8.61/tcp/61319/p2p/12D3KooWELrypWhpyUef9gtJr5TUne1173fG24QpMrRN62HN4VDk'

const HEART_BEAT_TIME = 10000
let intervalId_heartbeat // heart beat

let _HeartBeatTime = Date.now() - HEART_BEAT_TIME

const createNode = async ({ swarmKey, seed }) => {
    // const peerId = await createEd25519PeerId(seed)
    const node = await createLibp2p({
        // peerId,
        addresses: {
            listen: [
                // '/ip4/0.0.0.0/tcp/0',
                '/ip4/0.0.0.0/tcp/0/ws'
            ]
        },
        transports: [
            webSockets(),
            //   webRTC(),
            circuitRelayTransport({
                discoverRelays: 1,
            }),
        ],
        connectionEncryption: [noise()],
        streamMuxers: [yamux(), mplex()],
        services: {
            pubsub: gossipsub({ allowPublishToZeroPeers: true, emitSelf: false }),
            identify: identifyService()
        },
        peerDiscovery: [
            bootstrap({
                list: [
                    RELAY_SERVER
                ]
            })
        ],
        connectionProtector: preSharedKey({
            psk: swarmKey
        })
    })

    return node
}

const waitForChannelReady = async (node) => {
    const nodeId = node.peerId.toString()
    const did = peerIdToDid(nodeId)
    node.services.pubsub.subscribe(`${did}-channel`)
    let waitIntervalId_2
    const waitChannelReady = () => {
        return new Promise((resolve, reject) => {
            logger.debug('wait for channal is ready: ' + did)
            waitIntervalId_2 = setInterval(() => {
                node.services.pubsub.subscribe(`${did}-channel`)
                const peers = node.services.pubsub.getSubscribers(`${did}-channel`)
                logger.debug('channel peers', peers.map(item => item.toString()))
                if (peers.length > 0) {
                    logger.debug('channel is ready')
                    _HeartBeatTime = Date.now()
                    resolve()
                }
            }, 1000)
        })
    }
    await waitChannelReady()
    clearInterval(waitIntervalId_2)
}

const beginHeartBeat = (node, interval) => {
    const nodeId = node.peerId.toString()
    const did = peerIdToDid(nodeId)

    logger.debug('begin heart beat')
    node.services.pubsub.publish(`${did}-channel`, new TextEncoder().encode(JSON.stringify({ type: 'heart-beat' })));
    intervalId_heartbeat = setInterval(() => {
        logger.debug('send heart beat')
        node.services.pubsub.publish(`${did}-channel`, new TextEncoder().encode(JSON.stringify({ type: 'heart-beat' })));
    }, interval)
}

const dobootstrap = async (node) => {
    logger.debug('dial to bootstrap', RELAY_SERVER)
    const addr = await node.dial(multiaddr(RELAY_SERVER))
    logger.debug('connected to relay', RELAY_SERVER)
    const address = node.getMultiaddrs()
    for (let item of address) {
        logger.debug('multi address', item.toString())
    }
}

const _announceProfile = (node, { did, nickName }) => {
    let body = {
      type: 'update-profile',
      payload: { did, nickName }
    }
    node.services.pubsub.publish(`${did}-channel`, new TextEncoder().encode(JSON.stringify(body)));
}

const runClient = async (nickName) => {
    const swarmKey = uint8ArrayFromString(swarmKey_dev)
    const node = await createNode({ swarmKey })

    const nodeId = node.peerId.toString()
    console.log(`Node started with id ${nodeId}`)
    console.log('Listening on:')
    node.getMultiaddrs().forEach((ma) => console.log(ma.toString()))

    const did = peerIdToDid(nodeId)
    console.log('did:', did)

    beginHeartBeat(node, HEART_BEAT_TIME)
    dobootstrap(node)

    await waitForChannelReady(node)

    _announceProfile(node, { did, nickName })

}

// ;(async () => {
    // const range = []
    // const count = 10
    // for (let i = 0; i < count; i++) range.push(i+1)
    // const mapper = (i) => runClient('client_' + i)
    // pMap(range, mapper)
// })()

let args = process.argv
runClient('client_' + args[2])